Python'ın asyncio düşük seviyeli ağ programlamasında uzmanlaşın. Bu rehber, yüksek performanslı uygulamalar için Transport ve Protocol'leri pratik örneklerle inceliyor.
Python'ın Asyncio Transport Mimarisi: Düşük Seviyeli Ağ Programlamaya Derinlemesine Bakış
Modern Python dünyasında, asyncio
yüksek performanslı ağ programlamanın temel taşı haline geldi. Geliştiriciler genellikle, dikkat çekici bir kolaylıkla duyarlı uygulamalar oluşturmak için aiohttp
veya FastAPI
gibi kütüphanelerle birlikte async
ve await
kullanarak onun harika yüksek seviyeli API'leriyle başlarlar. asyncio.open_connection()
gibi fonksiyonlar tarafından sağlanan StreamReader
ve StreamWriter
nesneleri, ağ G/Ç işlemlerini yönetmek için harika derecede basit, sıralı bir yol sunar. Peki ya soyutlama yeterli olmadığında ne olur? Karmaşık, durum bilgisi olan veya standart dışı bir ağ protokolü uygulamanız gerekirse ne olur? Ya da temeldeki bağlantıyı doğrudan kontrol ederek performansın son damlasına kadar sıkıştırmanız gerekirse? İşte bu noktada asyncio'nun ağ yeteneklerinin gerçek temeli yatar: düşük seviyeli Transport ve Protocol API'si. İlk başta göz korkutucu görünse de, bu güçlü ikiliyi anlamak yeni bir kontrol ve esneklik seviyesinin kilidini açar ve hayal edilebilecek hemen hemen her ağ uygulamasını oluşturmanıza olanak tanır. Bu kapsamlı rehber, soyutlama katmanlarını aralayacak, Transport'lar ve Protocol'ler arasındaki simbiyotik ilişkiyi keşfedecek ve Python'da düşük seviyeli asenkron ağ programlamasında ustalaşmanız için sizi pratik örneklerle yönlendirecektir.
Asyncio Ağ Programlamanın İki Yüzü: Yüksek Seviye vs. Düşük Seviye
Düşük seviyeli API'lere derinlemesine dalmadan önce, asyncio ekosistemi içindeki yerlerini anlamak çok önemlidir. Asyncio, akıllıca, her biri farklı kullanım durumları için uyarlanmış iki ayrı ağ iletişimi katmanı sunar.
Yüksek Seviye API: Stream'ler
Genellikle "Stream'ler" olarak adlandırılan yüksek seviyeli API, çoğu geliştiricinin ilk karşılaştığı şeydir. asyncio.open_connection()
veya asyncio.start_server()
kullandığınızda, StreamReader
ve StreamWriter
nesneleri alırsınız. Bu API, basitlik ve kullanım kolaylığı için tasarlanmıştır.
- Emir Kipi Tarzı: Sıralı görünen kod yazmanıza olanak tanır. 100 bayt almak için
await reader.read(100)
, ardından bir yanıt göndermek içinwriter.write(data)
kullanırsınız. Buasync/await
deseni sezgiseldir ve hakkında akıl yürütmesi kolaydır. - Kullanışlı Yardımcılar:
readuntil(separator)
vereadexactly(n)
gibi, yaygın çerçeveleme görevlerini yerine getiren ve sizi arabellekleri manuel olarak yönetmekten kurtaran yöntemler sunar. - İdeal Kullanım Durumları: Basit istek-yanıt protokolleri (temel bir HTTP istemcisi gibi), satır tabanlı protokoller (Redis veya SMTP gibi) veya iletişimin öngörülebilir, doğrusal bir akışı izlediği her durum için mükemmeldir.
Ancak, bu basitliğin bir bedeli vardır. Akış tabanlı yaklaşım, istenmeyen mesajların herhangi bir zamanda gelebildiği yüksek düzeyde eşzamanlı, olay güdümlü protokoller için daha az verimli olabilir. Sıralı await
modeli, eşzamanlı okuma ve yazmaları yönetmeyi veya karmaşık bağlantı durumlarını idare etmeyi hantal hale getirebilir.
Düşük Seviye API: Transport'lar ve Protocol'ler
Bu, yüksek seviyeli Stream'ler API'sinin aslında üzerine inşa edildiği temel katmandır. Düşük seviyeli API, iki ayrı bileşene dayalı bir tasarım deseni kullanır: Transport'lar ve Protocol'ler.
- Olay Güdümlü Tarz: Sizin veri almak için bir fonksiyon çağırmanız yerine, olaylar meydana geldiğinde (örneğin, bir bağlantı kurulduğunda, veri alındığında) asyncio nesnenizdeki yöntemleri çağırır. Bu, geri arama (callback) tabanlı bir yaklaşımdır.
- Sorumlulukların Ayrılması: "Ne"yi "nasıl"dan temiz bir şekilde ayırır. Protocol, veriyle ne yapılacağını (uygulama mantığınız) tanımlarken, Transport verinin ağ üzerinden nasıl gönderilip alınacağını (G/Ç mekanizması) yönetir.
- Maksimum Kontrol: Bu API size arabelleğe alma, akış kontrolü (geri basınç - backpressure) ve bağlantı yaşam döngüsü üzerinde hassas kontrol sağlar.
- İdeal Kullanım Durumları: Özel ikili veya metin protokollerini uygulamak, binlerce kalıcı bağlantıyı yöneten yüksek performanslı sunucular oluşturmak veya ağ çerçeveleri ve kütüphaneleri geliştirmek için gereklidir.
Şöyle düşünün: Stream'ler API'si bir yemek kiti servisi sipariş etmek gibidir. Önceden porsiyonlanmış malzemeler ve takip etmesi kolay bir tarif alırsınız. Transport ve Protocol API'si ise profesyonel bir mutfakta ham malzemelerle çalışan ve sürecin her adımında tam kontrole sahip bir şef olmak gibidir. Her ikisi de harika bir yemek üretebilir, ancak ikincisi sınırsız yaratıcılık ve kontrol sunar.
Temel Bileşenler: Transport'lara ve Protocol'lere Daha Yakından Bakış
Düşük seviyeli API'nin gücü, Protocol ve Transport arasındaki zarif etkileşimden gelir. Düşük seviyeli herhangi bir asyncio ağ uygulamasında ayrı ama ayrılmaz ortaklardır.
Protocol: Uygulamanızın Beyni
Protocol, sizin yazdığınız bir sınıftır. asyncio.Protocol
'den (veya varyantlarından birinden) miras alır ve tek bir ağ bağlantısını yönetmek için durumu ve mantığı içerir. Bu sınıfı kendiniz başlatmazsınız; onu asyncio'ya (örneğin, loop.create_server
'a) sağlarsınız ve asyncio her yeni istemci bağlantısı için protokolünüzün yeni bir örneğini oluşturur.
Protokol sınıfınız, olay döngüsünün bağlantının yaşam döngüsünün farklı noktalarında çağırdığı bir dizi olay işleyici yöntemiyle tanımlanır. En önemlileri şunlardır:
connection_made(self, transport)
Yeni bir bağlantı başarıyla kurulduğunda tam olarak bir kez çağrılır. Bu sizin giriş noktanızdır. Bağlantıyı temsil eden transport
nesnesini aldığınız yer burasıdır. Genellikle self.transport
olarak ona bir referans kaydetmelisiniz. Arabellekleri ayarlamak veya karşı tarafın adresini günlüğe kaydetmek gibi bağlantı başına başlatma işlemlerini gerçekleştirmek için ideal bir yerdir.
data_received(self, data)
Protokolünüzün kalbi. Bu yöntem, bağlantının diğer ucundan yeni veri alındığında çağrılır. data
argümanı bir bytes
nesnesidir. TCP'nin bir mesaj protokolü değil, bir akış protokolü olduğunu hatırlamak çok önemlidir. Uygulamanızdan gelen tek bir mantıksal mesaj, birden çok data_received
çağrısına bölünebilir veya birden çok küçük mesaj tek bir çağrıda bir araya getirilebilir. Kodunuz bu arabelleğe almayı ve ayrıştırmayı yönetmelidir.
connection_lost(self, exc)
Bağlantı kapatıldığında çağrılır. Bu birkaç nedenle olabilir. Bağlantı temiz bir şekilde kapatılırsa (örneğin, diğer taraf kapatırsa veya siz transport.close()
çağırırsanız), exc
None
olacaktır. Bağlantı bir hata nedeniyle (örneğin, ağ arızası, sıfırlama) kapatılırsa, exc
hatayı detaylandıran bir istisna nesnesi olacaktır. Bu, temizlik yapmak, bağlantı kesilmesini günlüğe kaydetmek veya bir istemci oluşturuyorsanız yeniden bağlanmayı denemek için bir şanstır.
eof_received(self)
Bu daha incelikli bir geri arama yöntemidir. Diğer tarafın daha fazla veri göndermeyeceğini bildirdiğinde (örneğin, bir POSIX sisteminde shutdown(SHUT_WR)
çağırarak) çağrılır, ancak bağlantı sizin veri göndermeniz için hala açık olabilir. Bu yöntemden True
döndürürseniz, transport kapatılır. False
(varsayılan) döndürürseniz, transport'u daha sonra kendiniz kapatmaktan sorumlu olursunuz.
Transport: İletişim Kanalı
Transport, asyncio tarafından sağlanan bir nesnedir. Siz oluşturmazsınız; onu protokolünüzün connection_made
yönteminde alırsınız. Temeldeki ağ soketi ve olay döngüsünün G/Ç zamanlaması üzerinde yüksek seviyeli bir soyutlama görevi görür. Birincil görevi, veri gönderimini ve bağlantının kontrolünü yönetmektir.
Transport ile yöntemleri aracılığıyla etkileşim kurarsınız:
transport.write(data)
Veri göndermek için birincil yöntem. data
bir bytes
nesnesi olmalıdır. Bu yöntem engellemesizdir (non-blocking). Veriyi hemen göndermez. Bunun yerine, veriyi dahili bir yazma arabelleğine yerleştirir ve olay döngüsü bunu arka planda ağ üzerinden mümkün olduğunca verimli bir şekilde gönderir.
transport.writelines(list_of_data)
Bir dizi bytes
nesnesini arabelleğe tek seferde yazmanın daha verimli bir yolu, potansiyel olarak sistem çağrılarının sayısını azaltır.
transport.close()
Bu, nazik bir kapatma başlatır. Transport önce yazma arabelleğinde kalan verileri boşaltacak ve ardından bağlantıyı kapatacaktır. close()
çağrıldıktan sonra daha fazla veri yazılamaz.
transport.abort()
Bu, sert bir kapatma gerçekleştirir. Bağlantı derhal kapatılır ve yazma arabelleğinde bekleyen tüm veriler atılır. Bu, istisnai durumlarda kullanılmalıdır.
transport.get_extra_info(name, default=None)
İç gözlem için çok kullanışlı bir yöntem. Karşı tarafın adresi ('peername'
), temeldeki soket nesnesi ('socket'
) veya SSL/TLS sertifika bilgileri ('ssl_object'
) gibi bağlantı hakkında bilgi alabilirsiniz.
Simbiyotik İlişki
Bu tasarımın güzelliği, net, döngüsel bilgi akışıdır:
- Kurulum: Olay döngüsü yeni bir bağlantıyı kabul eder.
- Örnekleme: Döngü,
Protocol
sınıfınızın bir örneğini ve bağlantıyı temsil eden birTransport
nesnesi oluşturur. - Bağlantı Kurma: Döngü,
your_protocol.connection_made(transport)
çağırarak iki nesneyi birbirine bağlar. Protokolünüz artık veri göndermenin bir yoluna sahiptir. - Veri Alma: Ağ soketine veri geldiğinde, olay döngüsü uyanır, veriyi okur ve
your_protocol.data_received(data)
çağırır. - İşleme: Protokolünüzün mantığı alınan veriyi işler.
- Veri Gönderme: Mantığına dayanarak, protokolünüz bir yanıt göndermek için
self.transport.write(response_data)
çağırır. Veri arabelleğe alınır. - Arka Plan G/Ç: Olay döngüsü, arabelleğe alınan verinin transport üzerinden engellemesiz gönderimini yönetir.
- Sonlandırma: Bağlantı sona erdiğinde, olay döngüsü son temizlik için
your_protocol.connection_lost(exc)
çağırır.
Pratik Bir Örnek Oluşturma: Bir Yankı Sunucusu ve İstemcisi
Teori harikadır, ancak Transport'ları ve Protocol'leri anlamanın en iyi yolu bir şeyler inşa etmektir. Klasik bir yankı sunucusu ve buna karşılık gelen bir istemci oluşturalım. Sunucu bağlantıları kabul edecek ve aldığı her veriyi basitçe geri gönderecektir.
Yankı Sunucusu Uygulaması
Öncelikle, sunucu tarafı protokolümüzü tanımlayacağız. Temel olay işleyicilerini sergileyen dikkat çekici derecede basittir.
import asyncio
class EchoServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
# Yeni bir bağlantı kuruldu.
# Günlüğe kaydetmek için uzak adresi al.
peername = transport.get_extra_info('peername')
print(f"Bağlantı şuradan: {peername}")
# Daha sonra kullanmak üzere transport'u sakla.
self.transport = transport
def data_received(self, data):
# İstemciden veri alındı.
message = data.decode()
print(f"Alınan veri: {message.strip()}")
# Veriyi istemciye geri yankıla.
print(f"Geri yankılanıyor: {message.strip()}")
self.transport.write(data)
def connection_lost(self, exc):
# Bağlantı kapatıldı.
print("Bağlantı kapatıldı.")
# Transport otomatik olarak kapatılır, burada self.transport.close() çağırmaya gerek yok.
async def main_server():
# Sunucuyu süresiz olarak çalıştırmayı planladığımız için olay döngüsüne bir referans al.
loop = asyncio.get_running_loop()
host = '127.0.0.1'
port = 8888
# `create_server` coroutine'i sunucuyu oluşturur ve başlatır.
# İlk argüman, yeni bir protokol örneği döndüren çağrılabilir bir protocol_factory'dir.
# Bizim durumumuzda, sadece `EchoServerProtocol` sınıfını geçmek işe yarar.
server = await loop.create_server(
lambda: EchoServerProtocol(),
host,
port)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'{addrs} üzerinde hizmet veriliyor')
# Sunucu arka planda çalışır. Ana coroutine'i canlı tutmak için,
# asla tamamlanmayan bir şeye, örneğin yeni bir Future'a await edebiliriz.
# Bu örnek için, sadece "sonsuza kadar" çalıştıracağız.
async with server:
await server.serve_forever()
if __name__ == "__main__":
try:
# Sunucuyu çalıştırmak için:
asyncio.run(main_server())
except KeyboardInterrupt:
print("Sunucu kapatıldı.")
Bu sunucu kodunda, loop.create_server()
anahtardır. Belirtilen ana bilgisayar ve bağlantı noktasına bağlanır ve olay döngüsüne yeni bağlantıları dinlemeye başlamasını söyler. Her gelen bağlantı için, o belirli istemciye adanmış taze bir protokol örneği oluşturmak üzere protocol_factory
'mizi (lambda: EchoServerProtocol()
fonksiyonu) çağırır.
Yankı İstemcisi Uygulaması
İstemci protokolü biraz daha karmaşıktır çünkü kendi durumunu yönetmesi gerekir: hangi mesajı göndereceği ve işinin ne zaman "bittiğini" düşündüğü. Yaygın bir desen, istemciyi başlatan ana coroutine'e tamamlandığını bildirmek için bir asyncio.Future
veya asyncio.Event
kullanmaktır.
import asyncio
class EchoClientProtocol(asyncio.Protocol):
def __init__(self, message, on_con_lost):
self.message = message
self.on_con_lost = on_con_lost
self.transport = None
def connection_made(self, transport):
self.transport = transport
print(f"Gönderiliyor: {self.message}")
self.transport.write(self.message.encode())
def data_received(self, data):
print(f"Alınan yankı: {data.decode().strip()}")
def connection_lost(self, exc):
print("Sunucu bağlantıyı kapattı")
# Bağlantının kaybolduğunu ve görevin tamamlandığını bildir.
self.on_con_lost.set_result(True)
def eof_received(self):
# Bu, sunucu kapatmadan önce bir EOF gönderirse çağrılabilir.
print("Sunucudan EOF alındı.")
async def main_client():
loop = asyncio.get_running_loop()
# on_con_lost future'ı, istemcinin işinin tamamlandığını bildirmek için kullanılır.
on_con_lost = loop.create_future()
message = "Hello World!"
host = '127.0.0.1'
port = 8888
# `create_connection` bağlantıyı kurar ve protokolü bağlar.
try:
transport, protocol = await loop.create_connection(
lambda: EchoClientProtocol(message, on_con_lost),
host,
port)
except ConnectionRefusedError:
print("Bağlantı reddedildi. Sunucu çalışıyor mu?")
return
# Protokol bağlantının kaybolduğunu bildirene kadar bekle.
try:
await on_con_lost
finally:
# Transport'u nazikçe kapat.
transport.close()
if __name__ == "__main__":
# İstemciyi çalıştırmak için:
# Önce bir terminalde sunucuyu başlatın.
# Ardından, bu betiği başka bir terminalde çalıştırın.
asyncio.run(main_client())
Burada, loop.create_connection()
, create_server
'ın istemci tarafındaki karşılığıdır. Verilen adrese bağlanmaya çalışır. Başarılı olursa, EchoClientProtocol
'ümüzü örnekler ve connection_made
metodunu çağırır. on_con_lost
Future'ının kullanımı kritik bir desendir. main_client
coroutine'i bu future'ı await
eder, bu da protokol connection_lost
içinden on_con_lost.set_result(True)
çağırarak işinin bittiğini bildirene kadar kendi yürütmesini etkili bir şekilde duraklatır.
İleri Düzey Kavramlar ve Gerçek Dünya Senaryoları
Yankı örneği temelleri kapsar, ancak gerçek dünya protokolleri nadiren bu kadar basittir. Kaçınılmaz olarak karşılaşacağınız bazı daha ileri düzey konuları keşfedelim.
Mesaj Çerçeveleme ve Arabellek Yönetimi
Temellerden sonra kavranması gereken en önemli tek kavram, TCP'nin bir bayt akışı olduğudur. Doğal "mesaj" sınırları yoktur. Bir istemci "Merhaba" ve ardından "Dünya" gönderirse, sunucunuzun data_received
'ı bir kez b'HelloWorld'
ile, iki kez b'Hello'
ve b'World'
ile veya hatta kısmi verilerle birden çok kez çağrılabilir.
Protokolünüz "çerçevelemeden" — bu bayt akışlarını anlamlı mesajlara yeniden birleştirmekten — sorumludur. Yaygın bir strateji, yeni satır karakteri (\n
) gibi bir sınırlayıcı kullanmaktır.
İşte bir yeni satır bulana kadar veriyi arabelleğe alan ve her seferinde bir satırı işleyen değiştirilmiş bir protokol.
class LineBasedProtocol(asyncio.Protocol):
def __init__(self):
self._buffer = b''
self.transport = None
def connection_made(self, transport):
self.transport = transport
print("Bağlantı kuruldu.")
def data_received(self, data):
# Yeni veriyi dahili arabelleğe ekle
self._buffer += data
# Arabellekte olduğu kadar tam satırı işle
while b'\n' in self._buffer:
line, self._buffer = self._buffer.split(b'\n', 1)
self.process_line(line.decode().strip())
def process_line(self, line):
# Tek bir mesaj için uygulama mantığınız buraya gelir
print(f"Tam mesaj işleniyor: {line}")
response = f"İşlendi: {line}\n"
self.transport.write(response.encode())
def connection_lost(self, exc):
print("Bağlantı kaybedildi.")
Akış Kontrolünü Yönetme (Geri Basınç - Backpressure)
Uygulamanız veriyi transport'a ağın veya uzak eşin işleyebileceğinden daha hızlı yazıyorsa ne olur? Veri, transport'un dahili arabelleğinde birikir. Bu kontrolsüz bir şekilde devam ederse, arabellek süresiz olarak büyüyebilir ve mevcut tüm belleği tüketebilir. Bu sorun "geri basınç" (backpressure) eksikliği olarak bilinir.
Asyncio bunu yönetmek için bir mekanizma sağlar. Transport, kendi arabellek boyutunu izler. Arabellek belirli bir yüksek su işaretini geçtiğinde, olay döngüsü protokolünüzün pause_writing()
metodunu çağırır. Bu, uygulamanıza veri göndermeyi durdurması için bir sinyaldir. Arabellek bir düşük su işaretinin altına boşaltıldığında, döngü resume_writing()
çağırarak veri göndermenin tekrar güvenli olduğunu bildirir.
class FlowControlledProtocol(asyncio.Protocol):
def __init__(self):
self._paused = False
self._data_source = some_data_generator() # Bir veri kaynağı hayal edin
self.transport = None
def connection_made(self, transport):
self.transport = transport
self.resume_writing() # Yazma sürecini başlat
def pause_writing(self):
# Transport arabelleği dolu.
print("Yazma duraklatılıyor.")
self._paused = True
def resume_writing(self):
# Transport arabelleği boşaldı.
print("Yazma devam ettiriliyor.")
self._paused = False
self._write_more_data()
def _write_more_data(self):
# Bu bizim uygulamamızın yazma döngüsü.
while not self._paused:
try:
data = next(self._data_source)
self.transport.write(data)
except StopIteration:
self.transport.close()
break # Gönderilecek daha fazla veri yok
# Hemen duraklatmamız gerekip gerekmediğini görmek için arabellek boyutunu kontrol et
if self.transport.get_write_buffer_size() > 0:
self.pause_writing()
TCP'nin Ötesi: Diğer Transport'lar
TCP en yaygın kullanım durumu olsa da, Transport/Protocol deseni bununla sınırlı değildir. Asyncio diğer iletişim türleri için soyutlamalar sağlar:
- UDP: Bağlantısız iletişim için
loop.create_datagram_endpoint()
kullanırsınız. Bu size birDatagramTransport
verir vedatagram_received(data, addr)
veerror_received(exc)
gibi metodlarla birasyncio.DatagramProtocol
uygulamanız gerekir. - SSL/TLS: Şifreleme eklemek inanılmaz derecede basittir.
loop.create_server()
veyaloop.create_connection()
'a birssl.SSLContext
nesnesi geçirirsiniz. Asyncio, TLS el sıkışmasını otomatik olarak yönetir ve siz güvenli bir transport elde edersiniz. Protokol kodunuzun hiç değişmesi gerekmez. - Alt Süreçler: Çocuk süreçlerle standart G/Ç boruları aracılığıyla iletişim kurmak için,
loop.subprocess_exec()
veloop.subprocess_shell()
birasyncio.SubprocessProtocol
ile kullanılabilir. Bu, çocuk süreçleri tamamen asenkron, engellemesiz bir şekilde yönetmenize olanak tanır.
Stratejik Karar: Ne Zaman Transport, Ne Zaman Stream Kullanılmalı?
Elinizin altında iki güçlü API varken, kilit bir mimari karar iş için doğru olanı seçmektir. İşte karar vermenize yardımcı olacak bir rehber.
Stream'leri (StreamReader
/StreamWriter
) Şu Durumlarda Seçin...
- Protokolünüz basit ve istek-yanıt tabanlı olduğunda. Eğer mantık "bir istek oku, işle, bir yanıt yaz" ise, stream'ler mükemmeldir.
- İyi bilinen, satır tabanlı veya sabit uzunluklu mesaj protokolü için bir istemci oluşturuyorsanız. Örneğin, bir Redis sunucusu veya basit bir FTP sunucusu ile etkileşim kurmak.
- Kod okunabilirliğine ve doğrusal, emir kipi tarzına öncelik veriyorsanız. Stream'lerle
async/await
sözdizimi, asenkron programlamaya yeni başlayan geliştiriciler için genellikle anlaşılması daha kolaydır. - Hızlı prototipleme anahtar olduğunda. Sadece birkaç satır kodla stream'lerle basit bir istemci veya sunucu çalışır duruma getirebilirsiniz.
Transport ve Protocol'leri Şu Durumlarda Seçin...
- Sıfırdan karmaşık veya özel bir ağ protokolü uyguluyorsanız. Bu birincil kullanım durumudur. Oyun, finansal veri akışları, IoT cihazları veya eşler arası uygulamalar için protokolleri düşünün.
- Protokolünüz yüksek düzeyde olay güdümlü ve tamamen istek-yanıt tabanlı değilse. Sunucu istemciye herhangi bir zamanda istenmeyen mesajlar gönderebiliyorsa, protokollerin geri arama tabanlı doğası daha doğal bir uyum sağlar.
- Maksimum performans ve minimum ek yüke ihtiyacınız varsa. Protokoller size olay döngüsüne daha doğrudan bir yol sunar ve Stream'ler API'si ile ilişkili bazı ek yükleri atlar.
- Bağlantı üzerinde hassas kontrole ihtiyacınız varsa. Bu, manuel arabellek yönetimi, açık akış kontrolü (
pause/resume_writing
) ve bağlantı yaşam döngüsünün ayrıntılı yönetimini içerir. - Bir ağ çerçevesi veya kütüphanesi oluşturuyorsanız. Diğer geliştiriciler için bir araç sağlıyorsanız, Protocol/Transport API'sinin sağlam ve esnek doğası genellikle doğru temeldir.
Sonuç: Asyncio'nun Temellerini Benimsemek
Python'ın asyncio
kütüphanesi katmanlı bir tasarım şaheseridir. Yüksek seviyeli Stream'ler API'si erişilebilir ve üretken bir giriş noktası sağlarken, asyncio'nun ağ yeteneklerinin gerçek, güçlü temelini temsil eden düşük seviyeli Transport ve Protocol API'sidir. G/Ç mekanizmasını (Transport) uygulama mantığından (Protocol) ayırarak, sofistike ağ uygulamaları oluşturmak için sağlam, ölçeklenebilir ve inanılmaz derecede esnek bir model sunar.
Bu düşük seviyeli soyutlamayı anlamak sadece akademik bir egzersiz değildir; sizi basit istemci ve sunucuların ötesine geçmeye yetkilendiren pratik bir beceridir. Size herhangi bir ağ protokolüyle başa çıkma güvenini, baskı altında performans için optimize etme kontrolünü ve Python'da yeni nesil yüksek performanslı, asenkron hizmetler oluşturma yeteneğini verir. Bir dahaki sefere zorlu bir ağ problemiyle karşılaştığınızda, yüzeyin hemen altında yatan gücü hatırlayın ve zarif Transport ve Protocol ikilisine uzanmaktan çekinmeyin.